﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using System.IO;
using ResxCrunch.Business.Properties;
using System.Data;
using Microsoft.Win32;
using System.Security.AccessControl;
using ResxCrunch.Utils;

namespace ResxCrunch.Business.Managers
{
    /// <summary>
    /// Class working with serial numbers
    /// </summary>
    public class SerialMngr : Singleton<SerialMngr>
    {
        #region Constants
        /// <summary>
        /// Logger declaration
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        const string TableName = "License";
        const string ColNameData = "Data";        
        #endregion Constants

        #region Attributes
        #endregion Attributes

        #region Public Methods
        /// <summary>
        /// Verify if serial is valid
        /// </summary>
        /// <param name="serial">The serial.</param>
        /// <returns></returns>
        public bool VerifySerial(string serial)
        {
            if (
                Verify1stHash(serial) &&
                Verify2ndHash(serial))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Verifies if the license file exists and it is writeable
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public int VerifyLicenseFile(string fileName)
        {
            // -1 registered
            //  0 game over
            // +  trial

            try
            {
                if (!File.Exists(fileName))
                {
                    return 0;
                }

                DataTable table = new DataTable(TableName);

                table.ReadXml(fileName);

            }
            catch (Exception ex)
            {
                log.Error(string.Format("Error verifying license file '{0}'", fileName), ex);
            }

            return 0;
        }

        /// <summary>
        /// Extracts the registration data from file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="updateFileIfNecessary">if set to <c>true</c> [update file if minimum date is set (= first start of the application)].</param>
        /// <returns></returns>
        public RegistrationData ExtractRegistrationDataFromFile(string fileName, bool updateFileIfNecessary)
        {
            try
            {
                string serial;

                using (StreamReader sr = new StreamReader(fileName))
                {
                    serial = sr.ReadToEnd();
                }

                RegistrationData regData = this.ExtractRegistrationDataFromString(serial);

                bool generateSuccess = true;

                // If registration were active the license file would need update with the current date

                if (generateSuccess)
                {
                    return regData;
                }
                else
                {
                    log.ErrorFormat("Unable to update registration data file '{0}'", fileName);
                    return null;
                }
            }
            catch (Exception ex)
            {
                log.Error("Error while extracting registration data. File error", ex);
                return null;
            }
        }

        /// <summary>
        /// Extracts the registration data from an XML string.
        /// </summary>
        /// <param name="xmlInput">The XML input.</param>
        /// <returns></returns>
        public RegistrationData ExtractRegistrationDataFromString(string xmlInput)
        {
            try
            {
                RegistrationData regData = new RegistrationData();

                DataTable registrationInfoTable = new DataTable();
                registrationInfoTable.ReadXml(new StringReader(xmlInput));

                // Verify info
                if (registrationInfoTable.Rows.Count == 0 || !registrationInfoTable.Columns.Contains(ColNameData))
                {
                    log.Error("Error while extracting registration data because the input does not contain information");
                    return null;
                }

                string totalValue = registrationInfoTable.Rows[0][ColNameData].ToString();

                string[] parts = totalValue.Split(new char[] { '-', ' ' }, StringSplitOptions.RemoveEmptyEntries);

                // Verify if correct amount of data is present
                if (!parts.Length.Equals(3))
                {
                    log.Error("Error while extracting registration data. Corrupt data");
                    return null;
                }

                string encodedValue = parts[0];
                string decodedValue = EncryptionMngr.Instance.DecryptAesNumbersString(encodedValue);

                int position = 0;

                #region Extract data
                #region Extract date
                DateTime registrationDate;

                string yearStr = decodedValue.Substring(position, 4);
                position += 4;

                string monthStr = decodedValue.Substring(position, 2);
                position += 2;

                string dayStr = decodedValue.Substring(position, 2);
                position += 2;

                string hourStr = decodedValue.Substring(position, 2);
                position += 2;

                string minStr = decodedValue.Substring(position, 2);

                if (!DateTime.TryParse(string.Format("{0}-{1}-{2} {3}:{4}", yearStr, monthStr, dayStr, hourStr, minStr), out registrationDate))
                {
                    log.Error("Error while extracting registration data. Registration date is incorrect");
                    return null;
                }
                #endregion Extract date

                #region Extract serial
                position += 2;
                string serial = decodedValue.Substring(position, Utils.Constants.SerialLength);
                #endregion Extract serial

                #region Extract major version
                position += Utils.Constants.SerialLength;
                string majorStr = decodedValue.Substring(position, 2);
                int major;

                if (!int.TryParse(majorStr, out major))
                {
                    log.Error("Error while extracting registration data. Major version nr. is incorrect");
                    return null;
                }
                #endregion Extract major version

                #region Extract minor version
                position += 2;
                string minorStr = decodedValue.Substring(position, 2);
                int minor;

                if (!int.TryParse(minorStr, out minor))
                {
                    log.Error("Error while extracting registration data. minor version nr. is incorrect");
                    return null;
                }
                #endregion Extract minor version

                #region Extract email
                position += 2;
                string email = decodedValue.Substring(position);
                #endregion Extract email
                #endregion Extract data

                regData.RegistrationDate = registrationDate;
                regData.Serial = serial;
                regData.MajorVersion = major;
                regData.MinorVersion = minor;
                regData.Email = email;

                return regData;
            }
            catch (Exception ex)
            {
                log.Error("Error while extracting registration data", ex);
                return null;
            }
        }
        #endregion Public Methods

        #region Private methods
        private bool Verify1stHash(string serial)
        {
            string[] parts = serial.Split(new char[] { '-', ' ' });

            if (!parts.Length.Equals(3))
            {
                return false;
            }

            if ((EncryptionMngr.Instance.EncodeMD5(parts[0]) != parts[2]))
            {
                return false;
            }

            return true;
        }

        private bool Verify2ndHash(string serial)
        {
            string[] parts = serial.Split(new char[] { '-', ' ' });

            if (!parts.Length.Equals(3))
            {
                return false;
            }

            string decoded = EncryptionMngr.Instance.DecryptAesNumbersString(parts[0]);

            if (string.IsNullOrEmpty(decoded))
            {
                return false;
            }

            if ((EncryptionMngr.Instance.EncodeMD5(decoded) != parts[1]))
            {
                return false;
            }

            return true;
        }

        private string CreateDateEntry(DateTime date)
        {
            string dataValue = string.Format(
                                 "{0:0000}{1:00}{2:00}{3:00}{4:00}",
                                 date.Year,
                                 date.Month,
                                 date.Day,
                                 date.Hour,
                                 date.Minute);

            string encryptedValue = EncryptionMngr.Instance.EncryptAesToNumbersString(dataValue);
            string finalValue = string.Format("{0}-{1}", encryptedValue, EncryptionMngr.Instance.EncodeMD5(encryptedValue));

            return finalValue;
        }
        #endregion Private methods
    }
}


